import { useContext, useState } from "react";
import useNameInput from "../Commons/useNameInput";
import { MessageBar, MessageBarType, PrimaryButton, Stack, TextField } from "@fluentui/react";
import { camelToKebab, columnProps, copyPackageTools, ensureDirectoryExists, getFileWithoutSources, getNewestVersion, getServer2Path, isIgnoreFolder, stackStyles, stackTokens } from "../utils";
import useInstallPath from "../Commons/useInstallPath";
import usePluginInstallPath from "../Commons/usePluginInstallPath";
import { fs, path } from '@tauri-apps/api';
import { GlobalContext } from "../Context/global";
import { v4 } from "uuid";
import usePackageClassInput from "../Commons/usePackageClassInput";
import { RS } from "../../i18n/config";
const API_NAME = 'forguncy-server-api,forguncy-logger-abstractions,forguncy-plugin-common,forguncy-commands';
const API_DISPLAY_NAME = 'forguncy-commands';
const TEMPLATE_FILE_WHITE_LIST: { [key: string]: true } = {
    "COMMAND_PLUGIN_NAME.java": true,
    "Icon.png": true,
    "PluginConfig.json": true,
    "PluginLogo.png": true,
    "pom.xml": true
}
function Plugin() {
    const cnDefaultName = '我的插件';
    const descDefault = RS.descDefault;
    const [nameDom, name, nameValid] = useNameInput({ label: RS.Plugin_Label, initValue: 'MyPlugin' });
    const [cnName, setCnName] = useState(cnDefaultName);//可为空
    const [desc, setDesc] = useState(descDefault);//可为空
    const [forguncyInstallPathDom, forguncyPath, forguncyPathValid, jarVersion, _setForguncyPath] = useInstallPath({
        getDependenceVersion: tryGetJavaApiFile,
        apiName: API_NAME,
        apiDisplayName: API_DISPLAY_NAME
    })
    const [pluginInstallDom, installPath, installPathValid] = usePluginInstallPath({ label: RS.Plugin_Label, defaultPath: "forguncy-java-plugin" });
    const [PackageClassDom, packageClass, packageNameIsValid] = usePackageClassInput();
    const allValid = nameValid && forguncyPathValid && installPathValid && packageNameIsValid;
    const fullInstallPath = `${installPath}${name}`;
    const { dispatch } = useContext(GlobalContext);
    const create = async () => {
        const finalCnName = cnName || cnDefaultName;
        const finalDesc = desc || descDefault;
        const sourceCodeInstallPath = await path.join(fullInstallPath, name);
        try {
            await ensureDirectoryExists(sourceCodeInstallPath);
            const packagePath = await path.join(sourceCodeInstallPath, "src", "main", "java", ...packageClass.split('.'));
            await ensureDirectoryExists(packagePath);

            const thePath = await path.join(await path.resourceDir(), "command-plugin-template");
            await copyFilesRecursively(
                thePath, sourceCodeInstallPath, name, jarVersion,
                finalDesc, finalCnName, forguncyPath, packagePath, packageClass, thePath
            );
            await copyPackageTools(fullInstallPath, true);
        } catch (error: any) {
            dispatch?.({ type: 'errorMsg', payload: error.toString() });
            return;
        }
        dispatch?.({ type: 'createSuccess', payload: sourceCodeInstallPath })
    }
    return <>
        <div className="container">
            <form noValidate autoComplete="off">
                <Stack horizontal tokens={stackTokens} styles={stackStyles}>
                    <Stack {...columnProps}>
                        <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                            <div style={{ width: '48%' }}>{nameDom}</div>
                            <div
                                style={{ width: '48%', display: 'inline-block' }}>
                                <TextField
                                    maxLength={50}
                                    label={RS.Plguin_CN_Name}
                                    value={cnName}
                                    onChange={ev => setCnName((ev.target as HTMLInputElement).value)}
                                    placeholder={cnDefaultName}
                                />
                            </div>
                        </div>
                        {PackageClassDom}
                        <TextField
                            maxLength={50}
                            label={RS.Plguin_Desc}
                            value={desc}
                            onChange={ev => setDesc((ev.target as HTMLInputElement).value)}
                            placeholder={descDefault}
                        />
                        {forguncyInstallPathDom}
                        {pluginInstallDom}

                        {(!(allValid)) ? undefined : <MessageBar
                            messageBarType={MessageBarType.success}
                            isMultiline={true}
                        >
                            {RS.Project_Will_Be_Created_In}{fullInstallPath}
                        </MessageBar>}
                    </Stack>
                </Stack>
            </form>
            <div style={{ textAlign: 'center' }}>
                <PrimaryButton onClick={create} disabled={!(allValid)} style={{ margin: '36px 24px', width: '240px' }}>{RS.Create_Plugin}</PrimaryButton>
            </div>
        </div>
    </>
}
export default Plugin;


async function tryGetJavaApiFile(forguncyPath: string, prefix: string) {
    const jarPath = await path.join(forguncyPath, await getServer2Path(forguncyPath), 'javaAdapterServerBin');
    const exist = await fs.exists(jarPath);
    if (exist) {
        const apis = prefix.split(',')
        const files = await getFileWithoutSources(jarPath);
        const versions = apis.map(api => {
            const hits = files.filter(v => v.name?.indexOf(api) === 0 && v.name.split('.').pop() === 'jar');
            const versions = hits.map(hit => {
                const fileName = hit.name || '';
                const lastDotIndex = fileName.lastIndexOf('.');
                const fileNameWithoutExt = fileName.substring(0, lastDotIndex);
                const jarVersion = (fileNameWithoutExt.substring(api.length + 1));
                if (jarVersion.split('.').length === 4) {
                    return jarVersion;
                }
            })
            return getNewestVersion(versions)
        });
        const allEqual = versions.every(v => v === versions[0]);
        if (allEqual) {
            return versions[0] || ''
        }
    }
    return ''
}


async function copyFilesRecursively(sourcePath: string, targetPath: string, name: string, jarVersion: string,
    finalDesc: string, finalCnName: string, forguncyPath: string, packagePath: string, packageClass: string, root: string) {

    if (!(await fs.exists(sourcePath))) {
        return;
    }

    if (!(await fs.exists(targetPath))) {
        await fs.createDir(targetPath);
    }

    const files = await fs.readDir(sourcePath);

    files.forEach(async (file) => {
        const currentSource = await path.join(sourcePath, file.name || '');
        const currentTarget = await path.join(targetPath, file.name || '');

        const isDir = await getIsDir(currentSource);
        if (isDir) {
            copyFilesRecursively(currentSource, currentTarget, name, jarVersion, finalDesc, finalCnName, forguncyPath, packagePath, packageClass, root);
        } else {
            if (await isIgnoreFolder(file, root) || !TEMPLATE_FILE_WHITE_LIST[file.name || '']) {
            } else if (file.name === 'COMMAND_PLUGIN_NAME.java') {
                //主类的文件名 
                const uint8Array = await fs.readBinaryFile(currentSource);
                const decoder = new TextDecoder('utf-8');
                const decodedString = decoder.decode(uint8Array);
                const targetString = decodedString
                    .replace(/COMMAND_PLUGIN_NAME/g, name)
                    .replace(/ORG_EXAMPLE/g, packageClass)
                    .replace(/PLUGIN_CN_NAME/g, finalCnName);
                const newTarget = await path.join(packagePath, name + '.java');
                await fs.writeTextFile(newTarget, targetString);
            } else if (file.name === 'pom.xml') {
                const jarPath = (await path.join(forguncyPath, await getServer2Path(forguncyPath), 'javaAdapterServerBin')).split("\\").join("/");
                const javaPath = (await path.join(forguncyPath, await getServer2Path(forguncyPath), 'bpmJavaServerBin')).split("\\").join("/");
                const uint8Array = await fs.readBinaryFile(currentSource);
                const decoder = new TextDecoder('utf-8');
                const decodedString = decoder.decode(uint8Array);
                const targetString = decodedString.replace(/PLUGIN_DEP_VERSION/g, jarVersion)
                    .replace(/PLUGIN_KEBAB_NAME/g, camelToKebab(name))
                    .replace(/PLUGIN_ABS_PATH/g, jarPath)
                    .replace(/JAVA_PATH/g, javaPath);
                await fs.writeTextFile(currentTarget, targetString); //  pom 改 artifactId name
            } else if (file.name === "PluginConfig.json") {
                const uint8Array = await fs.readBinaryFile(currentSource);
                const decoder = new TextDecoder('utf-8');
                const decodedString = decoder.decode(uint8Array);
                const targetString = decodedString
                    .replace(/PLUGIN_DESC/g, finalDesc)
                    .replace(/PLUGIN_CN_NAME/g, finalCnName)
                    .replace(/PLUGIN_NAME/g, name)
                    .replace(/GUID/g, v4())
                    .replace(/DEP_VER/g, jarVersion);
                await fs.writeTextFile(currentTarget, targetString);
            } else {
                await fs.copyFile(currentSource, currentTarget);
            }
        }
    });

    async function getIsDir(currentSource: string) {
        try {
            await fs.readDir(currentSource);
            return true;
        } catch (error) {
            return false;
        }
    }
}
